package chapter2_nio.str;

import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class ReceiverUtil {
    int stepIndex;//用于计数数据帧。发送一次stepIndex+1
    ByteBuffer buffer;//用于存储对象数据(String对象或者普通java对象或者文件对象)
    ByteBuffer countBuffer;//用于存储对象的字节数(数据帧的字节数)
    boolean isCountDone = false;//用于判断是否将ByteBuffer countBuffer存储的对象的长度发送完毕

    Object obj;//要发送的对象(需要转为ByteBuffer赋值给buffer成员变量)

    public int getStepIndex() {//√ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √
        return stepIndex;
    }



    //下边是和SenderUtil不同的地方
    Class<?> clazz;//对象的类型

    /** i用于给stepIndex变量(计数数据帧)赋值，cls用于给clazz成员变量赋值(表示接收的对象类型) */
    ReceiverUtil(int i, Class<?>  cls){//√ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √
        this.clazz=cls;
        //countBuffer用于存储要接收的对象的字节数(数据帧的字节数)
        countBuffer=ByteBuffer.allocate(4);
        buffer=null;//Byte buffer用于存储接收对象的数据所以要清空
        obj=null;//Object obj用于用于存储接收对象所以要清空
        this.stepIndex=i;
    }

    public boolean recvFully(SocketChannel channel)throws  Exception{//√ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √
        //注意：此方法会无限循环调用(在Server类的service()方法中循环调用session方法(session方法又调用recvFully方法))
        //(还有Client类的communicate()方法中循环调用session方法(session方法又调用recvFully方法))
        //所以可能会多次执行channel.read(countBuffer);接收对象的长度到countBuffer中(长度接收完后再多次接收对象数据到buffer中)
        if(!isCountDone){
            //isCountDone= recvBufferFully(channel,countBuffer);
            channel.read(countBuffer);//用channel的read方法读取接收的对象长度数据存到countBuffer中
            System.out.println(countBuffer.getInt(0)+"长度");
            //判断对象长度数据是否接收完毕,如果还有未接收的数据
            //  则countBuffer就还有剩余的空间,则isCountDone为  !true
            isCountDone= !countBuffer.hasRemaining();
            return false;//返回false表示还没有接收完对象的长度数据
        }//isCountDone若为true则表示要接收的对象的字节数(数据帧的字节数)已经接收完毕
        System.out.println(countBuffer.getInt(0)+"长度222");
        if(buffer==null){//并且buffer为空就创建ByteBuffer buffer。大小为countBuffer中存储的总字节数
            buffer=ByteBuffer.allocate(countBuffer.getInt(0));
        }
        //return recvBufferFully(channel,buffer);
        channel.read(buffer);//用channel的read方法读取接收的对象数据存到buffer中
        return  !buffer.hasRemaining();//返回对象的数据是否接收完毕,如果还有未接收的数据
        //  则buffer就还有剩余的空间，就返回  !true 表示没有接收完对象的数据
        //注意：此方法会无限循环调用(在Server的service()方法中循环调用session方法(session方法又调用recvFully方法))
        //(还有Client类的communicate()方法中循环调用session方法(session方法又调用recvFully方法))
        //所以可能会多次执行channel.read(buffer)接收对象数据到buffer中
    }

    /*private boolean recvBufferFully(SocketChannel channel, ByteBuffer buf) throws IOException {
        channel.read(buf);//用channel的read方法读取接收的对象长度数据存到countBuffer中
        return !buf.hasRemaining();
    }*/


    Object getObject() throws Exception {//√ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √
        //判断ByteBuffer buffer的数据是否读取完毕
        //  !false: 表示如果读取完毕,就调用flip()方法将操作位置定位到数据的开头
        if(!buffer.hasRemaining()){//并将buffer中存储的对象数据转化为Object对象(准备用SocketChannel发送了)
            buffer.flip();//flip(夫来坡):翻转
            obj=byteBufferToObject(buffer);
        }//flip(): 将操作位置定位到数据的开头，将limit设置为数据的末尾(具体如下)
        return obj;//(limit设置为position的位置，position设置为0)
    }

    private Object byteBufferToObject(ByteBuffer buffer)throws  Exception{
        byte[] bytes=buffer.array();//将ByteBuffer buffer中要发送的对象数据转换为字节数组bytes
        if(clazz.equals(String.class))//如果发送的对象是String类型，
            return new String(bytes,"UTF-8");//就将字节数组转换为String类型返回
        else//否则(要发送的对象类型不是String类型而是普通java对象)，就将字节数组转换为普通java对象返回
           return NIOSendReceiveUtil_6.byteArrayToObjSerializable(bytes);
    }


}
